// SINSTALL.CPP
// Written by chensu


#include <afxwin.h>
#include "resource.h"
#include "sinstall.h"


//*****************************************************************************
//-----------------------------------------------------------------------------
CSInstallApp SInstallApp;
//-----------------------------------------------------------------------------
//*****************************************************************************


//*****************************************************************************
//-----------------------------------------------------------------------------
BOOL CSInstallApp::InitInstance()
{
	// only a single instance is allowed
	ASSERT(::AfxGetAppName() != NULL);
	VERIFY(::CreateMutex(NULL, FALSE, ::AfxGetAppName()) != NULL);
	if (::GetLastError() == ERROR_ALREADY_EXISTS)
	{
		return FALSE;
	}

    // continue?
	if (::AfxMessageBox(IDS_WELCOME, MB_YESNO | MB_ICONQUESTION) != IDYES)
	{
		return FALSE;
	}
    
	// install it
	::AfxMessageBox(this->InstallIt() ? IDS_SUCC : IDS_FAIL,
					MB_OK | MB_ICONINFORMATION);

    return FALSE;
}
//-----------------------------------------------------------------------------
BOOL CSInstallApp::InstallIt() const
{
	CWaitCursor curWait;

	// construct the full path name of the screen saver
	CString strSavePath;
	VERIFY(::GetWindowsDirectory(strSavePath.GetBuffer(MAX_PATH), MAX_PATH) != 0);
	strSavePath.ReleaseBuffer();

	if (strSavePath.Right(1) != _T("\\"))
	{
		strSavePath += _T('\\');
	}
	
	strSavePath += CString(reinterpret_cast<LPCTSTR>(IDS_SCRFILENAME));

	// install the screen saver and select it as default
	return this->InstallFile(strSavePath, MAKEINTRESOURCE(ID_RESSAVER)) &&
		   this->SelectAsDefault(strSavePath);
}
//-----------------------------------------------------------------------------
BOOL CSInstallApp::InstallFile(LPCTSTR lpszSavePath, LPCTSTR lpszRes) const
{
	ASSERT(::AfxIsValidString(lpszSavePath));

	CFileStatus status;
	if (CFile::GetStatus(lpszSavePath, status))
	{
		// The file exists, we'll check versions first.
		CString strTempPath, strTempFile;
		VERIFY(::GetTempPath(MAX_PATH, strTempPath.GetBuffer(MAX_PATH)) != 0);
		VERIFY(::GetTempFileName(strTempPath, _T("scs"), 0,
								 strTempFile.GetBuffer(MAX_PATH)) != 0);

		// Save the resource to the temp file
		if (!this->SaveResourceToFile(strTempFile, lpszRes))
		{
			return FALSE;
		}

		// Get the version information
		DWORD dwOrigVer = 0;
		this->GetFileVersionField(lpszSavePath, _T("FileVersion"),
								  NULL, 0, &dwOrigVer);
		DWORD dwNewVer = 0;
		this->GetFileVersionField(strTempFile, _T("FileVersion"),
								  NULL, 0, &dwNewVer);

		// delete the temp file
		VERIFY(::DeleteFile(strTempFile));

#if 0
		if (dwOrigVer >= dwNewVer || (dwOrigVer == 0 && dwNewVer == 0))
		{
			return TRUE;  // if there's an already up to date version.
		}
#else
		if (dwOrigVer > dwNewVer)
		{
			return TRUE;  // if there's an already up to date version.
		}
#endif

		// delete the old version file
		if (!::DeleteFile(lpszSavePath))
		{
			return FALSE;  // the file may be in use
		}
	}

	// create the screen saver file
	return this->SaveResourceToFile(lpszSavePath, lpszRes);
}
//-----------------------------------------------------------------------------
BOOL CSInstallApp::SelectAsDefault(LPCTSTR lpszScr) const 
{
	ASSERT(::AfxIsValidString(lpszScr));

	CString strShortScr;
	return ::GetShortPathName(lpszScr,
							  strShortScr.GetBuffer(MAX_PATH), MAX_PATH)
			&&
			::WritePrivateProfileString(_T("boot"), _T("SCRNSAVE.EXE"),
										strShortScr, _T("system.ini"))
			&&
			::SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, TRUE);
}
//-----------------------------------------------------------------------------
BOOL CSInstallApp::SaveResourceToFile(LPCTSTR lpszSavePath, LPCTSTR lpszRes) const
{
	ASSERT(::AfxIsValidString(lpszSavePath));

	const HRSRC hRsrc = ::FindResource(::AfxGetResourceHandle(), lpszRes, RT_RCDATA);
	if (hRsrc == NULL)
	{
		return FALSE;
	}

	const DWORD dwSize = ::SizeofResource(::AfxGetResourceHandle(), hRsrc);
	const HGLOBAL hResData = ::LoadResource(::AfxGetResourceHandle(), hRsrc);
	const LPVOID lpData = ::LockResource(hResData);

	if (lpData == NULL)
	{
		return FALSE;
	}

	try
	{
		CFile file(lpszSavePath,
				   CFile::modeCreate | CFile::modeWrite |
				   CFile::shareExclusive);
		file.Write(lpData, dwSize);
	}
	catch (CFileException *pEx)
	{
		pEx->Delete();

		return FALSE;
	}

	return TRUE;
}
//-----------------------------------------------------------------------------
// GETFILEVERSIONFIELD: returns version info.
// lpszFilename: filename
// lpszInfo: information to get back. Things like "FileVersion"
// lpszRet: a buffer in which to return the thing.
// nLen: length of this buffer, in characters.
// lpdwVer: if non-null,
//          then we'll try to parse a string like "1,7,5,0" into 0x01070500
int CSInstallApp::GetFileVersionField(LPCTSTR lpszFilename,
									  LPCTSTR lpszInfo,
									  LPTSTR lpszRet, int nLen,
									  LPDWORD lpdwVer) const
{
	ASSERT(::AfxIsValidString(lpszFilename));
	ASSERT(::AfxIsValidString(lpszInfo));
	ASSERT_NULL_OR_POINTER(lpdwVer, DWORD);

	if (lpdwVer != NULL)
	{
		*lpdwVer = 0;
	}

	DWORD dwhVersion = 0;
	const DWORD dwVerInfoSize =
		::GetFileVersionInfoSize(const_cast<LPTSTR>(lpszFilename), &dwhVersion);
	if (dwVerInfoSize == 0)
	{
		return 0;
	}

	const LPVOID lpData = new char[dwVerInfoSize];

	int nVerLen = 0;
	if (::GetFileVersionInfo(const_cast<LPTSTR>(lpszFilename), dwhVersion,
							 dwVerInfoSize, lpData))
	{
		LPVOID lpTransBlock = NULL;
		UINT nVerSize = 0;
		if (::VerQueryValue(lpData,
							_T("\\VarFileInfo\\Translation"),
							&lpTransBlock,
							&nVerSize))
		{
			// Swap the words so wsprintf will print
			// the lang-charset in the correct format.
			*static_cast<LPDWORD>(lpTransBlock) =
				MAKELONG(HIWORD(*static_cast<LPDWORD>(lpTransBlock)),
						 LOWORD(*static_cast<LPDWORD>(lpTransBlock)));

			CString strSubBlock;
			strSubBlock.Format(_T("\\StringFileInfo\\%08lx\\%s"),
							   *static_cast<LPDWORD>(lpTransBlock),
							   lpszInfo);

			LPTSTR lpVer = NULL;
			if (::VerQueryValue(lpData,
								const_cast<LPTSTR>(
									static_cast<LPCTSTR>(strSubBlock)),
								reinterpret_cast<LPVOID *>(&lpVer),
								&nVerSize))
			{
				if (lpszRet != NULL)
				{
					ASSERT(::AfxIsValidAddress(lpszRet, nLen * sizeof(TCHAR)));

					nVerLen = ::lstrlen(lpVer);

					if (nVerLen > nLen - 1)
					{
						nVerLen = nLen - 1;
					}

					// ie. we'll try to copy the \0 in vData,
					// but we'll leave space for the thing.
					for (int ni = 0; ni < nVerLen; ni++)
					{
						lpszRet[ni] = lpVer[ni];
					}

					lpszRet[nVerLen] = _T('\0');
				}

				// The following code converts a string like 1.0 or 1.1.5 into
				// a DWORD where the first digit is the leading byte, and so on.
				// It discards version numbers greater than 255, and discards
				// anything after the fourth version sub-part.
				if (lpdwVer != NULL)
				{
					DWORD dwv = 0;

					BOOL bEnd=FALSE;
					LPTSTR lpchFirst = lpVer;
					LPTSTR lpchNext = lpchFirst;
					int nPlaces = 0;
					while (!bEnd)
					{
						while (*lpchNext != _T('\0') &&
							   *lpchNext != _T('.') &&
							   *lpchNext != _T(','))
						{
							lpchNext++;
						}

						if (*lpchNext == _T('\0'))
						{
							bEnd = TRUE;
						}

						*lpchNext = _T('\0');

						int ni = 0;
						int nRes = ::_stscanf(lpchFirst, _T("%i"), &ni);
						if (nRes == 0)
						{
							bEnd = TRUE;
						}
						else
						{
							dwv = (dwv << 8) | (ni & 255);
							nPlaces++;
						}

						lpchNext++;
						lpchFirst = lpchNext;

						if (nPlaces == 4)
						{
							bEnd = TRUE;
						}
					}

					for (int ni = nPlaces; ni < 4; ni++)
					{
						dwv = dwv << 8;
					}

					*lpdwVer = dwv;
				}
			}
		}
	}

	delete []lpData;

	return nVerLen;
}
//-----------------------------------------------------------------------------
//*****************************************************************************


// End of SINSTALL.CPP
